/* d_conductor.c
*	Client user program of distributed conductor in a train simulation
*	
*	Simulates a conductor
*/
 
#include "syscall.h"
 
const int MAXSTOPS = 10; /*How many Stops*/
const int MAX_WAITER = 3; /*how many Waiters*/
const int MAX_CHEF = 2; /*how many Chefs */
const int Conductor_Free = 0;/*Conductor is free*/
const int Conductor_Steward_Busy = 1; /*Conductor is busy interacting with a waiter*/
const int Conductor_Chef_Busy = 2; /*Conductor is busy interacting with a chef*/
const int MAX_DCSeat = 10 ;/*Maximum Seats in Dining Car*/
const int INVENTORY_FACTOR = 3 ;/*how many times the same food's been ordered will cause low inventory*/
const int Steward_Free = 0; /*Steward is free*/
const int Chef_Free = 0; /*Chef is free*/
const int Waiter_Free =0 ; /*Waiter is free*/

/* Lock */
int g_conductorBusyLock;
int g_reportInventoryLock;
int g_reportDCMoneyLock;

/* CV */
int g_conductorBusyCV;
int g_reportInventoryCV;
int g_reportDCMoneyCV;

/* MV */
int numPassengersOnTrain;
int g_totalTicketPrice;
int currentStop;
int g_conductorBusy;
int g_placeInventoryOrder;
int g_foodInventory[4];
int g_DCMoney;
int g_stewardBusy;
int g_waiterBusy[3];
int g_checkWaiterLineLength[3];
int g_hungryPassengerIndex[3];
int g_desiredFood[3];
int g_isCooked_PW[3];
int waiterStdWaiting[3];
int g_psgBill[3];
int g_chefBusy[2];
int g_checkChefLineLength[2];
int g_orderingPassengerIndex[2];
int g_cookFood[2];
int g_isCooked[2];
int g_stewardBusy;
int g_paidPassengerIndex;
int g_paidFood;
int g_waiterIndex;
int g_chefIndex;
int g_kitchenClean;
int g_cookTimes;
int g_placeInventoryOrder;
int g_lowInventory;
int g_psgIndex;
int g_DCLineLength;
int g_avaDCSeat;
int g_askingPassengerIndex;
int rejectedCount;
int enterDC;
int callingWaiter;
int passengersWaitingAtStop[10];
int checkerBusy; 
int tcTrainWaiting;  
int seatedPassengers;
int numFirstClassThatBoarded;
int numEconClassThatBoarded;
int attendantBroadcast;
int porterBroadcast;  
int inventoryChecked;
int numNeedHelpWithLuggage;
int g_caIndex; 
int g_porterIndex;

void Conductor(void);
void Dummy(void);
void setupMonitors(void);
void itoa(char arr[], int size, int val);

int main(int argc, char **argv)
{
	void (*myConductor)(void);
	void (*myDummy)(void); /*For some reason if you fork just once, nothing works*/
	
	myConductor = &Conductor;
	myDummy = &Dummy;
	
	setupMonitors(); /*Requesting all the locks, CVs, and MVs needed by Ticket Checker client*/
	Fork(myConductor);
	Fork(myDummy);

	Exit(0);
}

void Dummy()
{
	PrintOut("Dummy Thread\n", 13);
    Exit(0);		
}

void setupMonitors(){

	int i;
	char indexBuf[2];
	char nameBuf[4]; /*a prefix will be put in first two slots, then the local index*/
	
	for (i = 0; i < 4 ; i ++){ 
		itoa (indexBuf, 2, i);
		nameBuf[0] = 'f';
		nameBuf[1] = 'i';
		nameBuf[2] = indexBuf[0];
		nameBuf[3] = indexBuf[1]; /* Example name: "fi2"*/
		g_foodInventory[i] = CreateMV(nameBuf, 4, 1);
		SetMV(g_foodInventory[i], MAX_DCSeat + INVENTORY_FACTOR );
	}

	for(i=0; i<MAX_WAITER; i++)
	{
		itoa (indexBuf, 2, i);
		nameBuf[0] = 'w';
		nameBuf[1] = 'b';
		nameBuf[2] = indexBuf[0];
		nameBuf[3] = indexBuf[1]; /* Example name: "wb2"*/
		
		g_waiterBusy[i] = CreateMV (nameBuf, 4, 1);
		SetMV(g_waiterBusy[i],Waiter_Free);

		nameBuf[0] = 'c';
		nameBuf[1] = 'w';
		g_checkWaiterLineLength[i] = CreateMV (nameBuf, 4, 1);
		SetMV(g_checkWaiterLineLength[i],0);

		nameBuf[0] = 'h';
		nameBuf[1] = 'p';
		g_hungryPassengerIndex[i] = CreateMV (nameBuf, 4, 1);
		SetMV(g_hungryPassengerIndex[i],0);

		nameBuf[0] = 'd';
		nameBuf[1] = 'f';
		g_desiredFood[i] = CreateMV (nameBuf, 4, 1);
		SetMV(g_desiredFood[i],0);
 
		nameBuf[0] = 'p';
		nameBuf[1] = 'w';
		g_isCooked_PW[i] = CreateMV (nameBuf, 4, 1);
		SetMV(g_isCooked_PW[i],0);

		nameBuf[0] = 'w';
		nameBuf[1] = 's';
		waiterStdWaiting[i] = CreateMV (nameBuf, 4, 1);
		SetMV(waiterStdWaiting[i],0);
	
		nameBuf[0] = 't';
		nameBuf[1] = 'o'; /*i know it looks weird but don't change it*/
		g_psgBill[i] = CreateMV (nameBuf, 4, 1);
		SetMV(g_psgBill[i],0);
	}

	for(i=0; i<MAX_CHEF; i++)
	{
		itoa (indexBuf, 2, i);
		nameBuf[0] = 'c';
		nameBuf[1] = 'b';
		nameBuf[2] = indexBuf[0];
		nameBuf[3] = indexBuf[1]; /* Example name: "cb1"*/
		
		g_chefBusy[i] = CreateMV (nameBuf, 4, Chef_Free);

		nameBuf[0] = 'c';
		nameBuf[1] = 'c';
		g_checkChefLineLength[i] = CreateMV (nameBuf, 4, 1);
		SetMV(g_checkChefLineLength[i],0);
		
		nameBuf[0] = 'o';
		nameBuf[1] = 'p';
		g_orderingPassengerIndex[i] = CreateMV (nameBuf, 4, 1);
		SetMV(g_orderingPassengerIndex[i],0);

		nameBuf[0] = 'c';
		nameBuf[1] = 'f';
		g_cookFood[i] = CreateMV (nameBuf, 4, 1);
		SetMV(g_cookFood[i],0);

		nameBuf[0] = 'i';
		nameBuf[1] = 'c';
		g_isCooked[i] = CreateMV (nameBuf, 4, 1);
		SetMV(g_isCooked[i],0);
	}
	
	g_conductorBusyLock = CreateLock("g_conBusyLock", 13, 1);
	g_reportInventoryLock = CreateLock("g_repInvLock", 12, 1);
	g_reportDCMoneyLock = CreateLock("g_repDCMoneyLock", 16, 1);
	
	g_conductorBusyCV = CreateCV("g_conBusyCV", 17, 1);
	g_reportInventoryCV= CreateCV("g_repInvCV", 16, 1);
	g_reportDCMoneyCV = CreateCV("g_repDCMoneyCV", 14, 1);
	
	numPassengersOnTrain = CreateMV("numPsgsOnTrain",  14, 1);
	SetMV(numPassengersOnTrain,0);
	numNeedHelpWithLuggage = CreateMV("numNeedHelpWithLug", 18, 1);
	SetMV(numNeedHelpWithLuggage, 0);
	g_caIndex = CreateMV("g_caIndex", 9, 1);
	SetMV(g_caIndex, 0);
	g_porterIndex = CreateMV("g_porterIndex", 13, 1);
	SetMV(g_porterIndex, 0);
	g_totalTicketPrice = CreateMV("g_totalTicketPrice", 18, 1);
	currentStop = CreateMV("currentStopMV", 13, 1);
	SetMV(currentStop,0);
	g_conductorBusy = CreateMV("g_conductorBusy", 15, 1);
	SetMV(g_conductorBusy, Conductor_Free);
	
	g_DCMoney = CreateMV("g_DCMoney", 9, 1);
	SetMV(g_DCMoney, 0);	
	
	g_stewardBusy = CreateMV("g_stewardBusy", 13, 0);
	SetMV(g_stewardBusy, Steward_Free);

	g_paidPassengerIndex = CreateMV("g_pPIndex", 9, 0);
	SetMV(g_paidPassengerIndex,0);
	g_paidFood = CreateMV("g_paidFood", 10, 0);
	SetMV(g_paidFood,0);

	g_waiterIndex = CreateMV ("g_waiterIndex", 13, 1);
	SetMV(g_waiterIndex,0);

	g_chefIndex = CreateMV ("g_chefIndex", 11, 1);
	SetMV(g_chefIndex,0);

	g_kitchenClean = CreateMV("g_kitchenClean", 14, 0);
	SetMV(g_kitchenClean,0);

	g_cookTimes = CreateMV("g_cookTimes", 11, 0);
	SetMV(g_cookTimes,0);

	g_placeInventoryOrder = CreateMV("g_placeInvOrder", 15, 0);
	SetMV(g_placeInventoryOrder,0);

	g_lowInventory = CreateMV("g_lowInventory", 14, 0);
	SetMV(g_lowInventory,0);
	inventoryChecked = CreateMV("inventoryChecked", 16, 0);

	g_psgIndex = CreateMV (g_psgIndex, 4, 1);
	SetMV(g_psgIndex,0);

	g_DCLineLength = CreateMV("g_DCLineLength", 14, 0);
	SetMV(g_DCLineLength, 0);

	g_avaDCSeat = CreateMV("g_avaDCSeat", 11, 0);
	SetMV(g_avaDCSeat, 0);
	g_askingPassengerIndex = CreateMV("g_aPIndex", 9, 0);
	SetMV(g_askingPassengerIndex, 0);

	rejectedCount = CreateMV("rejectedCount", 13, 0);
	SetMV(rejectedCount, 0);

	enterDC = CreateMV("enterDC", 7, 0);
	SetMV(enterDC, 0);

	callingWaiter = CreateMV("callingWaiter", 13, 1);
	SetMV(callingWaiter, 0);

	for(i=0; i<MAXSTOPS; i++)
	{
		itoa (indexBuf, 2, i);
		nameBuf[0] = 'w';
		nameBuf[1] = 't';
		nameBuf[2] = indexBuf[0];
		nameBuf[3] = indexBuf[1]; /* Example name: "wt6"*/
		
		passengersWaitingAtStop[i] = CreateMV (nameBuf, 4, 1);
	}
	
	
	checkerBusy = CreateMV("checkerBusyMV", 13, 1);
	SetMV(checkerBusy, 1); /*starts out busy*/

	/*--Ticket Checker waiting on Train--*/
	tcTrainWaiting = CreateMV("tcTrainWaitingMV", 16, 1);
	SetMV(tcTrainWaiting,0); 

	seatedPassengers = CreateMV("seatedPassengersMV", 18, 1);
	SetMV(seatedPassengers,0); 

	numFirstClassThatBoarded = CreateMV("nFCThatBoardedMV", 16, 1);
	SetMV(numFirstClassThatBoarded,0);

	numEconClassThatBoarded = CreateMV("nECThatBoardedMV", 16, 1);
	SetMV(numEconClassThatBoarded,0); 

	attendantBroadcast = CreateMV("attBroadcastMV", 14, 1);
	SetMV(attendantBroadcast,0); 

	porterBroadcast = CreateMV("porterBroadcastMV", 17, 1);
	SetMV(porterBroadcast,0); 
}

void Conductor(){
	int currentDCMoney = 0;
	int currentTicketMoney = 0;
	int coachAttendentTipMoney = 0;
	int totalRevenue = 0;
	int lastStop = -1;
	char psgNumBuf[2]; /*numbers of Passengers*/
	char ticketBuf[7]; /*Amount earned from Tickets*/
	char DCMoneyBuf[7]; /*Amount earned from Dining Car*/
	char moneyBuf[7]; /*total money earned*/
	int i; /*interacter for fill up the */
	int randomNum; /*Random time for reporting the Revenue*/
	
	/*Start stop*/
	itoa(psgNumBuf, 2, GetMV(numPassengersOnTrain));
	itoa(ticketBuf, 7 , GetMV(g_totalTicketPrice));
	PrintOut("Total Passengers in the Train [0] = [",37);
	PrintOut(psgNumBuf, 2);
	PrintOut("]\n", 2);
	PrintOut("Train [0] Ticket Revenue = [",28);
	PrintOut(ticketBuf,7);
	PrintOut("]\n",2);
	
	while (GetMV(currentStop)<MAXSTOPS)
    {
		if(lastStop<GetMV(currentStop))
		{
			/*Conductor statistics report*/
			itoa(psgNumBuf, 2, GetMV(numPassengersOnTrain));
			itoa(ticketBuf, 7 , GetMV(g_totalTicketPrice));
			PrintOut("Total Passengers in the Train [0] = [",37);
			PrintOut(psgNumBuf, 2);
			PrintOut("]\n", 2);
			PrintOut("Train [0] Ticket Revenue = [",28);
			PrintOut(ticketBuf,7);
			PrintOut("]\n",2);
			lastStop = GetMV(currentStop);
		}

		if(GetMV(g_conductorBusy) == Conductor_Free) 
		{
			Acquire(g_conductorBusyLock,0);
			/*Signal anyone waiting for me to be free that I am free*/
			Signal(g_conductorBusyCV, g_conductorBusyLock,0); 
			/*Wait for someone to want to interact. They will set g_conductorBusy.*/
			Wait(g_conductorBusyCV, g_conductorBusyLock,0); 

			/*Let other person know I know we are interacting*/
			Release(g_conductorBusyLock,0);
		}
		
		/*Find out who it is*/
		if(GetMV(g_conductorBusy) == Conductor_Chef_Busy) /*If it's the Chef, wait for him to tell you ask for inventory*/
		{
			/* **Monitor - Calling for Ordering inventory from Chef*/
			Acquire(g_reportInventoryLock,0); /* Ready for ordering*/
			/*no order has been called*/
			SetMV(g_placeInventoryOrder, 1); /* I call the order!!*/
			
			Signal(g_reportInventoryCV, g_reportInventoryLock,0); 
			
			/*Since we order the food
			  When Train Arrives at the stop, we get the food to inventory
			  total = how many in seats Dining Car + Factor*/
			for (i = 0; i < 4 ; i ++){ 
				SetMV(g_foodInventory[i], MAX_DCSeat + INVENTORY_FACTOR) ;
			}
			SetMV(g_conductorBusy, Conductor_Free);

			Release(g_reportInventoryLock,0);
		}
		else if(GetMV(g_conductorBusy) == Conductor_Steward_Busy)
		{	
			/* **Monitor - Receiving amount earned in Dining Car from Steward*/
			Acquire(g_reportDCMoneyLock,0);
			Signal(g_reportDCMoneyCV, g_reportDCMoneyLock,0); /*signal*/
			Wait(g_reportDCMoneyCV, g_reportDCMoneyLock,0); /*waiting for Steward reporting amount*/
			/*here, wake up*/
			currentDCMoney = GetMV(g_DCMoney);
			SetMV(g_stewardBusy, Steward_Free); /*Set him to be free after this interaction*/
			Signal(g_reportDCMoneyCV, g_reportDCMoneyLock,0); /*signal Steward*/
			itoa (DCMoneyBuf, 7, currentDCMoney);
			PrintOut("Train [0] Dining Car revenue = [", 32);
			PrintOut(DCMoneyBuf, 7);
			PrintOut("]\n", 2);
			totalRevenue = currentDCMoney + GetMV(g_totalTicketPrice); /*Add dining to total*/
			Release(g_reportDCMoneyLock,0);
		}

		/*Generating a random number to decide if announce revenue */
		randomNum = RandomInt()%1000;
		if(randomNum>900)
		{
			/*Announce revenue*/
			itoa (moneyBuf, 7, totalRevenue);
			PrintOut("Total Train [0] Revenue = [", 27);
			PrintOut(moneyBuf, 7);
			PrintOut("]\n", 2);
		}
    }
	
	Exit(0);
}
int myexp ( int count ) {
  int i, val=1;
  for (i=0; i<count; i++ ) {
    val = val * 10;
  }
  return val;
}

void itoa( char arr[], int size, int val ) {
  int i, max, dig, subval, loc;
  for (i=0; i<size; i++ ) {
    arr[i] = '\0';
  }

  for ( i=1; i<=10; i++ ) {
    if (( val / myexp(i) ) == 0 ) {
      max = i-1;
      break;
    }
  }

  subval = 0;
  loc = 0;
  for ( i=max; i>=0; i-- ) {
    dig = 48 + ((val-subval) / myexp(i));
    subval += (dig-48) * myexp(i);
    arr[loc] = dig;
    loc++;
  }

  return;
}